package cyb;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import org.dom4j.util.StringUtils;
import org.tinspin.index.kdtree.KDIterator;
import org.tinspin.index.kdtree.KDTree;
import org.tinspin.index.kdtree.Node;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

public class CoLocation {
    public static double minX = 999; // X 经度
    public static double maxX = 0;
    public static double minY = 999; // Y 纬度
    public static double maxY = 0;
    public static Properties.Method method = Properties.Method.JOIN_LESS;  //挖掘的co-location模式 方法
    //    public static Properties.Method method = Properties.Method.BOTH;  //挖掘的co-location模式 方法
    public static double lng = Util.getlngBydistance(Properties.R); //  经度 0-180 横坐标
    public static double lat = Util.getlatBydistance(Properties.R); //  纬度 0-90 纵坐标
    public static String poiExcelPath = "C:\\Users\\chenyb\\Desktop\\co-location\\data\\北京POI.xlsx";
    public static String poiTxtPath = "C:\\Users\\chenyb\\Desktop\\co-location\\data\\BeiJingPOIAll.txt";
    public static String poiTypePath = "C:\\Users\\chenyb\\Desktop\\co-location\\data\\POItype.txt";
    public static String poiTxtFinalPath = "C:\\Users\\chenyb\\Desktop\\co-location\\data\\POI.txt";
    public static String makedataTxtFinalPath = "C:\\Users\\chenyb\\Desktop\\co-location\\data\\" +
            Properties.dataFrom + "_POI_" + Properties.maxNum + "_" + Properties.make_featureNum + ".txt";
    public static String neighborPath = "C:\\Users\\chenyb\\Desktop\\co-location\\data\\" + Properties.dataFrom + "_neighbor_"
            + Properties.maxNum + "_" + Properties.R + "_" + Properties.support + ".txt";
    public static String resultPath = "C:\\Users\\chenyb\\Desktop\\co-location\\data\\result_"
            + Properties.maxNum + "_" + Properties.R + "_" + Properties.support + "_" + method + ".txt";

    /**
     * 1.将excel数据集转成txt数据集
     * txt数据格式 (id,name,type,X,Y) BeiJingPOIAll.txt
     * 坐标不能重叠，所以去除重叠的点。 385615条 =》323215条记录
     */
    public static void excel2txt(String poiExcelPath, String poiTxtPath) {
        if (FileUtil.exist(poiTxtPath)) {
            System.out.println("========1.POI excel已被读取===========");
            return;
        }
        System.out.println("========1.正在读取POI excel数据===========");
        long startTime = System.currentTimeMillis();
        ExcelReader reader = ExcelUtil.getReader(poiExcelPath, 0);
        //设置标题别名的目的在于读取的excel标题是中文，一一对应上javabean中的字段。如果excel标题就是字段名，则无需设置别名
        reader.addHeaderAlias("NAME", "name")
                .addHeaderAlias("TYPE", "type")
                .addHeaderAlias("X", "X")
                .addHeaderAlias("Y", "Y");
        List<POI> poiList = reader.read(0, 1, POI.class);
        long endTime = System.currentTimeMillis();
        Set poiSet = new HashSet();
        System.out.println("========1.读取POI excel数据成功，耗时：" + (endTime - startTime) / 1000 + "秒 ,共" + poiList.size() + "条记录===========");
//        System.out.println(poiList.subList(0, 3));
        int row = 1;
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(poiTxtPath))) {
            for (POI poi : poiList) {
                if (poi.getType() == null || poi.getType().trim().equals("")) {
                    continue;
                }
                Double x = poi.getX();
                Double y = poi.getY();
                String a = x + "," + y;
                if (!poiSet.contains(a)) {
                    poiSet.add(a);
                    bw.write(row + "," + poi.getName() + "," + poi.getType().split(";")[2] + "," + poi.getX() + "," + poi.getY());
                    bw.newLine(); //换行用
                }
                row++;

            }
        } catch (Exception e) {
            System.out.println("第" + row + "行数据异常：" + poiList.get(row));
            e.printStackTrace();
        }
    }

    /**
     * 2.提取所有type并编码，将id换成类 格式
     * txtFinal数据格式 (id,name,typecode,X,Y) POI.txt POItype.txt
     */
    public static void txtProceed(String poiTxtPath) {
        if (FileUtil.exist(poiTxtPath)) {
            System.out.println("========2.POI 已被预处理===========");
            return;
        }
        System.out.println("========2.正在预处理POI数据===========");
        long startTime = System.currentTimeMillis();
        List<String> poiList = null;
        try {
            poiList = Files.readAllLines(Paths.get(poiTxtPath));
        } catch (IOException e) {
            e.printStackTrace();
        }
        //读取地点类型
        Map<String, Integer> typeList = new HashMap<>();
        for (String poiStr : poiList) {
            String[] poiInfo = poiStr.split(",");
            String type = poiInfo[2];
            if (typeList.get(type) == null) {
                typeList.put(type, 1);
            } else {
                typeList.put(type, typeList.get(type) + 1);
            }
        }
        System.out.println(typeList);
        System.out.println(typeList.keySet().size());
        //对地点类型 编码
        Map<String, Integer> typeCode = new HashMap<>();
        int code = 1;
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(poiTypePath))) {
            for (String type : typeList.keySet()) {
                typeCode.put(type, code);
                bw.write(code + "," + type);
                bw.newLine(); //换行用
                code++;
            }
        } catch (Exception e) {
            System.out.println("第" + code + "行数据异常：");
            e.printStackTrace();
        }
        //更新txt文件
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(poiTxtFinalPath))) {
            for (String poiStr : poiList) {
                String[] poiInfo = poiStr.split(",");
                String type = poiInfo[2];
                bw.write(poiInfo[0] + "," + poiInfo[1] + "," + typeCode.get(type) + "," + poiInfo[3] + "," + poiInfo[4]);
                bw.newLine(); //换行用
                code++;
            }
        } catch (Exception e) {
            System.out.println("第" + code + "行数据异常：");
            e.printStackTrace();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("========2.预处理POI数据成功，耗时：" + (endTime - startTime) + "毫秒 ,共" + poiList.size() + "条记录===========");
    }

    /**
     * ==== 未使用
     * 暴力解法 获取各点的满足R距离阈值的邻近关系
     * TODO 考虑转化成矩阵运算/位运算，加快速度（也适合无距离阈值情况）
     */
    public static void getNeighborRelation(Map<String, List<String>> typeMap, Map<String, POI> poiMap, Float threshold) {
        for (int i1 = 1; i1 < typeMap.size() + 1; i1++) {
            List<String> starList = typeMap.get(i1);
            for (int i2 = 0; i2 < starList.size(); i2++) {
                String star = starList.get(i2);

                for (int j1 = i1 + 1; j1 < typeMap.size() + 1; j1++) {
                    List<String> neighborList = typeMap.get(j1);
                    for (int j2 = i2; j2 < starList.size(); j2++) {
                        String neighbor = starList.get(j2);
                        System.out.println("比对" + i1 + "特征的" + i2 + "记录和" + j1 + "特征的" + j2 + "记录");
                    }
                }
            }
        }
    }

    public static Map<String, POI> getPOIFromFile(int maxNum) throws IOException {

        Map<String, POI> poiMap = new HashMap<>();
        List<String> poiStrList = null;
        if (Properties.dataFrom.equals("make")){
            poiStrList = Files.readAllLines(Paths.get(makedataTxtFinalPath));
        }else{
            poiStrList = Files.readAllLines(Paths.get(poiTxtFinalPath));
        }
        int num = 1;
        for (String poiStr : poiStrList) {
            String[] poiInfo = poiStr.split(",");
            String id = poiInfo[0];
            String name = poiInfo[1];
            String type = poiInfo[2];
            Double X = Double.valueOf(poiInfo[3]);
            Double Y = Double.valueOf(poiInfo[4]);

            POI poi = new POI(id, name, type, X, Y);
            poiMap.put(id, poi);
            if (num == maxNum) {
                break;
            }
            num++;
        }
        return poiMap;
    }

    /**
     * （key：某个特征实例如a1 ；value：该特征实例的邻居map）-不包含比自己特征小的邻居
     * （key：a1邻居集的特征如B；value：特征为B的邻居）
     * 返回示例
     * {"a1":
     * {"B":
     * "b3,b2";
     * "C":
     * "c1,c2"
     * } 邻居集是乱序的
     * }
     */
    public static Map<String, Map<String, String>> getOldNeighborsFromFile(Map<String, POI> poiMap) throws IOException {
        Map<String, Map<String, String>> neighborMap = new HashMap<>();
        List<String> neighborStrList = Files.readAllLines(Paths.get(neighborPath));
        for (String str : neighborStrList) {
            String[] split = str.split(":");
            String starId = split[0];
            if (split.length > 1) {
                POI starPoi = poiMap.get(starId);
                String starType = starPoi.getType();

                Map<String, String> neighborTypeMap = new HashMap<>();
                for (String neighborId : split[1].split(",")) {
                    POI neighborPoi = poiMap.get(neighborId);
                    String neighborType = neighborPoi.getType();
                    // -不包含比自己特征小的邻居
                    if (Integer.valueOf(starType) < Integer.valueOf(neighborType)) {
                        String neighborStr = neighborTypeMap.get(neighborType);
                        if (neighborStr == null) {
                            neighborStr = neighborId;
                        } else {
                            neighborStr = neighborStr + "," + neighborId;
                        }
                        neighborTypeMap.put(neighborType, neighborStr);
                    }
                }
                if (neighborTypeMap.size() != 0) {
                    neighborMap.put(starId, neighborTypeMap);
                }
            } else {// 将没有邻居的poi点筛除
                poiMap.remove(starId);
            }
        }
        return neighborMap;
    }

    /**
     * 3.建KD树
     * kd tree的key： 经纬度 ，value： id （Integer）
     * 因为搜索结果只包含value，所以要用poiMap保存所有poi信息
     * <p>
     * 经纬度覆盖范围： (115.775922,40.441686)(117.502298,41.044505)
     * 因为纬度不同时，1个经度差的距离会变，所以采用最低纬度40.441686时的1个经度差的距离 用于距离转经纬度
     */
    public static edu.wlu.cs.levy.CG.KDTree<String> buildOldKDTree(List<String> poiList, Map<String, POI> poiMap, int maxNodeNum) throws Exception {
        long start = System.currentTimeMillis();
        edu.wlu.cs.levy.CG.KDTree<String> old_kdTree = new edu.wlu.cs.levy.CG.KDTree(2);
        int num = 1;
        for (String poiStr : poiList) {
            String[] poiInfo = poiStr.split(",");
            String id = poiInfo[0];
            String name = poiInfo[1];
            String type = poiInfo[2];
            Double X = Double.valueOf(poiInfo[3]);
            Double Y = Double.valueOf(poiInfo[4]);

            if (X > maxX) maxX = X;
            if (X < minX) minX = X;
            if (Y > maxY) maxY = Y;
            if (Y < minY) minY = Y;

            old_kdTree.insert(new double[]{X, Y}, id);
            POI poi = new POI(id, name, type, X, Y);
            poiMap.put(id, poi);
            if (num == maxNodeNum) {
                break;
            }
            num++;
        }
        System.out.println("========3.old kd-Tree建树完成，耗时: " + (System.currentTimeMillis() - start) + "毫秒，树节点共"
                + old_kdTree.size() + "个   => 经纬度覆盖范围： (" + minX + "," + minY + ")" + "(" + maxX + "," + maxY + ")");
        return old_kdTree;
    }

    /**
     * 3.建KD树
     * kd tree的key： 经纬度 ，value： id,type（String）
     * 因为搜索结果包含key value，所以不用poiMap
     */
    public static KDTree buildNewKDTree(List<String> poiList) {
        long start = System.currentTimeMillis();
        KDTree<String> new_kdTree = KDTree.create(2, Util::getEarthDistance);
        Set poiSet = new HashSet();
        for (String poiStr : poiList) {
            String[] poiInfo = poiStr.split(",");
            Integer id = Integer.valueOf(poiInfo[0]);
            String name = poiInfo[1];
            String type = poiInfo[2];
            Double X = Double.valueOf(poiInfo[3]);
            Double Y = Double.valueOf(poiInfo[4]);

            if (X > maxX) maxX = X;
            if (X < minX) minX = X;
            if (Y > maxY) maxY = Y;
            if (Y < minY) minY = Y;

            String a = X + "," + Y;
            if (!poiSet.contains(a)) {
                poiSet.add(a);
                new_kdTree.insert(new double[]{X, Y}, id + "," + type);
            }
        }
        System.out.println("new kd-Tree建树完成，耗时: " + (System.currentTimeMillis() - start) + "毫秒，树节点共"
                + new_kdTree.size() + "个" + " || kd树深度：" + new_kdTree.getDepth() + "  => 经纬度覆盖范围： (" + minX + "," + minY + ")" + "(" + maxX + "," + maxY + ")");
        return new_kdTree;
    }

    public static void getOldNeighbors(edu.wlu.cs.levy.CG.KDTree<String> oldKdTree, Map<String, POI> poiMap) throws Exception {
        Long start = System.currentTimeMillis();
        int num = 1;

        try (BufferedWriter bw = new BufferedWriter(new FileWriter(neighborPath))) {
            for (POI star : poiMap.values()) {
                String starId = star.getId();
                String starType = star.getType();
                Double starX = star.getX();
                Double starY = star.getY();

                List<String> old_query = oldKdTree.range(new double[]{starX - lng, starY - lat}, new double[]{starX + lng, starY + lat});
                StringBuffer starNeighborList = new StringBuffer();
                for (String neighborId : old_query) {
                    POI neighbor = poiMap.get(neighborId);
                    //排除同特征poi
                    if (!neighbor.getType().equals(starType)) {
                        //排除在矩形内，圆外的poi
                        //TODO 排除了比当前点id小的点
                        if (Util.getDistance(neighbor.getX(), neighbor.getY(), starX, starY) <= Properties.R) {
                            starNeighborList.append(neighbor.getId() + ",");
                        }
                    }
                }
                //去除不含邻居集，且
                if (!starNeighborList.toString().equals("")) {
                    starNeighborList.deleteCharAt(starNeighborList.length() - 1);
                    bw.write(starId + ":" + starNeighborList.toString());
                    bw.newLine(); //换行用
                }
                if (num % 5000 == 0) {
                    System.out.println("=> old kd-Tree " + num + "个节点邻居搜索完成");
                }
                num++;
            }
        } catch (Exception e) {
            System.out.println("搜索邻居 数据异常！！！");
            e.printStackTrace();
        }
        System.out.println("========4.old kd-Tree 所点邻居搜索完成，耗时: " + (System.currentTimeMillis() - start) + "毫秒");
    }

    public static void getNewNeighbors(List<String> poiList, KDTree<String> newKdTree) {
        Long start = System.currentTimeMillis();
        int num = 1;
        for (String poiStr : poiList) {
            String[] starInfo = poiStr.split(",");
            Integer starId = Integer.valueOf(starInfo[0]);
            String starType = starInfo[2];
            Double starX = Double.valueOf(starInfo[3]);
            Double starY = Double.valueOf(starInfo[4]);

            KDIterator<String> query = newKdTree.query(new double[]{starX - lng, starY - lat},
                    new double[]{starX + lng, starY + lat});
            List<Integer> starNeighborList = new LinkedList<>();
            while (query.hasNext()) {
                Node<String> neighbor = query.next();
                double[] neighborXY = neighbor.point();
                String neighborInfo = neighbor.value();
                Integer neighborId = Integer.valueOf(neighborInfo.split(",")[0]);
                String neighborType = neighborInfo.split(",")[1];
                //排除同特征poi
                if (!neighborType.equals(starType)) {
                    //排除在矩形内，圆外的poi
                    // TODO 没有排除比当前点id小的点
                    if (Util.getEarthDistance(neighborXY, new double[]{starX, starY}) <= Properties.R) {
                        starNeighborList.add(neighborId);
                    }
                }
            }
//            System.out.println(starNeighborList);
            if (num % 5000 == 0) {
                System.out.println("=> new kd-Tree " + num + "个节点邻居搜索完成");
                break;
            }
            num++;
        }
        System.out.println("new kd-Tree 所点邻居搜索完成，耗时: " + (System.currentTimeMillis() - start) + "毫秒");
    }

    public static Map<String, List<String>> gen1Co(Map<String, POI> poiMap) {
        Map<String, List<String>> oneCoMap = new HashMap<>();
        for (POI poi : poiMap.values()) {
            String type = poi.getType();
            String id = poi.getId();

            List<String> poiList = oneCoMap.get(type);
            if (poiList == null) {
                poiList = new LinkedList<>();
                oneCoMap.put(type, poiList);
            }
            poiList.add(id);
        }
        return oneCoMap;
    }

    public static String getCoDetail(Map<String, List<String>> nCoMap, int n) {
        int instanceNum = 0;
        for (List<String> list : nCoMap.values()) {
            instanceNum += list.size();
        }
        return (" => " + n + "阶模式数：" + nCoMap.size() + " ,总实例数：" + instanceNum);
    }


    /**
     * 检查n阶模式的所有表实例，特征是否错误，是否成团
     */
    public static boolean checkAllCliqueInstance(Map<String, List<String>> nPlusCoMap, Map<String, POI> poiMap) {
        for (Map.Entry<String, List<String>> entry2 : nPlusCoMap.entrySet()) {
            String nPlusCoType = entry2.getKey();
            String[] typeArr = entry2.getKey().split(",");
            Integer jie = typeArr.length;
            List<String> nPlusCoList = entry2.getValue();

            for (String nPlusCo : nPlusCoList) {
                String[] coArr = nPlusCo.split(",");
                for (int i = 0; i < jie; i++) {
                    POI poi1 = poiMap.get(coArr[i]);
                    if (!poi1.getType().equals(typeArr[i])) {
                        System.out.println("类型不匹配！！" + nPlusCoType + " => " + nPlusCo + " 里的" + typeArr[i] + " => " + coArr[i]);
                        return false;
                    }
                    for (int j = 0; j < jie; j++) {
                        if (i < j) {
                            POI poi2 = poiMap.get(coArr[j]);
                            if (Util.getDistance(poi1.getX(), poi1.getY(), poi2.getX(), poi2.getY()) > Properties.R) {
                                System.out.println("距离不匹配！！");
                                return false;
                            }
                        }
                    }
                }
            }
        }
        return true;
    }

    public static void main(String[] args) throws Exception {
        Map<String, List<String>> joinBase_nCoMap = new HashMap<>();
        Map<String, List<String>> joinLess_nCoMap = new HashMap<>();
        Map<String, Map<String, String>> neighborMap;
        Map<String, POI> poiMap = new HashMap<>();
        //1.处理excel转成txt
        excel2txt(poiExcelPath, poiTxtPath);
        //2.对坐标去重，地点类型编码
        txtProceed(poiTxtPath);

        if (!FileUtil.exist(neighborPath)) {
            //2.1.建KD树，共20多万个点，测试选用前1万个
            edu.wlu.cs.levy.CG.KDTree<String> old_KdTree = buildOldKDTree(Files.readAllLines(Paths.get(poiTxtFinalPath)), poiMap, Properties.maxNum);
            //2.2 获取邻居集，保存成txt
            getOldNeighbors(old_KdTree, poiMap);
        } else {
            poiMap = getPOIFromFile(Properties.maxNum);
        }

        //3.从保存的txt获取poi数据集和 星型邻居集（只包含特征比自己大的）
        neighborMap = getOldNeighborsFromFile(poiMap);
        System.out.println("========Final.从保存的txt获取数据集 和 邻居集===========");
        System.out.println("=> 读取数据集：poi点数：" + poiMap.size());
        System.out.println("=> 读取邻居集：poi点数：" + neighborMap.size());


        if (method == Properties.Method.JOIN_BASE || method == Properties.Method.BOTH) {
            System.out.println("==========================join-base===============================");
            //1.获取1阶co-location oneCoMap(key：特征 value：特征 实例集)
            long start = System.currentTimeMillis();
            Map<String, List<String>> oneCoMap = gen1Co(poiMap);
            System.out.println("========1.获取1阶co-location 耗时 " + (System.currentTimeMillis() - start) + " 毫秒" + getCoDetail(oneCoMap, 1));

            //2.构建2阶co-location twoCoMap(key：特征对 value：特征对 实例集)
            // 组合算法：(1) 只找比自己typecode大的(2层循环 n平方时间复杂度)
            //TODO 组合算法 回溯法 n*（n-1）/2
            start = System.currentTimeMillis();
            joinBase_nCoMap = JoinBase.genAll2Co(poiMap, oneCoMap);
            System.out.println("========2.获取2阶co-location 耗时 " + (System.currentTimeMillis() - start) + " 毫秒" + getCoDetail(joinBase_nCoMap, 2));

            //3.构建n阶co-location
            // 组合算法：固定前n-1阶
            for (int i = 3; i <= Properties.maxJie; i++) {
                start = System.currentTimeMillis();
                joinBase_nCoMap = JoinBase.genAllNPlusCo(joinBase_nCoMap, poiMap, oneCoMap);
                System.out.println("========" + i + ".获取" + i + "阶co-location耗时 " + (System.currentTimeMillis() - start) + " 毫秒"
                        + getCoDetail(joinBase_nCoMap, i));
                if (joinBase_nCoMap.size() == 0) {
                    break;
                }
            }

            //4.验证n阶模式 所有表实例是否成团，特征是否正确
            start = System.currentTimeMillis();
            boolean success = checkAllCliqueInstance(joinBase_nCoMap, poiMap);
            System.out.println("========Final.验证所有表实例是否成团：" + success
                    + "，耗时 " + (System.currentTimeMillis() - start) + " 毫秒");
        }
        if (method == Properties.Method.JOIN_LESS || method == Properties.Method.BOTH) {
            System.out.println("==========================join-less===============================");
            System.out.println("=> 读取邻居集");

            //1.获取1阶co-location oneCoMap(key：特征 value：特征 实例集)
            long start = System.currentTimeMillis();
            Map<String, List<String>> oneCoMap = gen1Co(poiMap);
            System.out.println("========1.获取1阶co-location耗时 " + (System.currentTimeMillis() - start) + " 毫秒" + getCoDetail(oneCoMap, 1));

            //2. 2阶co-location模式    =>  从每个点的邻居集找，生成2阶模式,因为肯定成团所以不粗筛
            start = System.currentTimeMillis();
            Map<String, List<String>> twoCoMap = JoinLess.genAll2Co(oneCoMap, poiMap, neighborMap);
            System.out.println(getCoDetail(twoCoMap, 2));
            JoinLess.cut(twoCoMap, oneCoMap, poiMap);
            System.out.println("========2.获取2阶co-location耗时 " + (System.currentTimeMillis() - start) + " 毫秒" + getCoDetail(twoCoMap, 2));

            joinLess_nCoMap = twoCoMap;
            //3. n阶co-location模式    =>  从每个点的邻居集找，生成n阶模式,要粗筛
            for (int i = 3; i <= Properties.maxJie; i++) {
                start = System.currentTimeMillis();
                joinLess_nCoMap = JoinLess.genAllNPlusCo(oneCoMap, twoCoMap, poiMap, neighborMap, joinLess_nCoMap);
                if (joinLess_nCoMap.size() == 0) {
                    break;
                }
                System.out.println(getCoDetail(joinLess_nCoMap, i));
                JoinLess.cut(joinLess_nCoMap, oneCoMap, poiMap);
                System.out.println("========" + i + ".获取" + i + "阶co-location耗时 " + (System.currentTimeMillis() - start) + " 毫秒"
                        + getCoDetail(joinLess_nCoMap, i));
            }
            //4.验证n阶模式 所有表实例是否成团，特征是否正确
            start = System.currentTimeMillis();
            boolean success = checkAllCliqueInstance(joinLess_nCoMap, poiMap);
            System.out.println("========Final.验证所有表实例是否成团：" + success
                    + "，耗时 " + (System.currentTimeMillis() - start) + " 毫秒");
        }


        if (method == Properties.Method.BOTH) {
            System.out.println("==========================算法结果比较==========================");
            boolean diff = Util.compareDiff(joinBase_nCoMap.keySet(), joinLess_nCoMap.keySet(), false);
            if (diff) {
                System.out.println("join-base 和 join-less 结果不同！！！");
                System.out.println(joinBase_nCoMap.get("109,173,537"));
            } else {
                System.out.println("join-base 和 join-less 结果一致！！！");
            }
        }
    }
}
